#include "CCanvas.h"
CCanvas::CCanvas()
{
    int i;
    lpBrushes[0] = CreateSolidBrush(RGB(192, 192, 192));
    lpBrushes[1] = CreateSolidBrush(RGB(192, 192, 255));
    lpBrushes[2] = CreateSolidBrush(RGB(255, 192, 192));
    lpBrushes[3] = CreateSolidBrush(RGB(255, 192, 255));
    lpBrushes[4] = CreateSolidBrush(RGB(192, 255, 192));
    lpBrushes[5] = CreateSolidBrush(RGB(192, 255, 255));
    lpBrushes[6] = CreateSolidBrush(RGB(255, 255, 192));
    lpBrushes[7] = CreateSolidBrush(RGB(64, 64, 64));
    lpBrushes[8] = CreateSolidBrush(RGB(64, 64, 255));
    lpBrushes[9] = CreateSolidBrush(RGB(255, 64, 64));
    lpBrushes[10] = CreateSolidBrush(RGB(255, 64, 255));
    lpBrushes[11] = CreateSolidBrush(RGB(64, 255, 64));
    lpBrushes[12] = CreateSolidBrush(RGB(64, 255, 255));
    lpBrushes[13] = CreateSolidBrush(RGB(255, 255, 64));
    iBaseWidth = COLUMN_INTERVAL * 3;
    iColumnTop = BASE_LINE - COLUMN_LENGTH;
    iColumeDiameter = COLUMN_RADIUS * 2;
    for (i = 0; i < 7; ++i)
    {
        lpiPlateRadius[6 - i] = MAX_PLATE - i * PLATE_DIFF;
    }
    iHand = 0;
    iPlateShape = ID_RECTANGLE;
    iSelect = -1;
    m_hFont = CreateFont(40, 15, 0, 0, 1, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_ROMAN, "Times New Roman");
}

CCanvas::~CCanvas()
{
}


void CCanvas::DrawBases(HDC hdc)
{
    int i;
    SelectObject(hdc, GetStockObject(BLACK_PEN));
    SelectObject(hdc, GetStockObject(WHITE_BRUSH));
    for (i = 0; i < 3; ++i)
    {
        Rectangle(hdc, LEFT_COLUMN - COLUMN_RADIUS + i * COLUMN_INTERVAL, iColumnTop, LEFT_COLUMN - COLUMN_RADIUS + i * COLUMN_INTERVAL + iColumeDiameter, iColumnTop + COLUMN_LENGTH + PLATE_THICKNESS / 2);
    }
    Rectangle(hdc, LEFT_BASE, BASE_LINE, LEFT_BASE + iBaseWidth, BASE_LINE + PLATE_THICKNESS);
}

void CCanvas::DrawRectangularPlates(HDC hdc)
{
    int i, j, k;
    SelectObject(hdc, GetStockObject(BLACK_PEN));
    for (i = 0; i < 3; ++i)
    {
        for (j = 0; j < hanoi.m_lpiHeight[i]; ++j)
        {
            k = hanoi.m_lpiColumn[i * MAX_LAYERS + j];
            if (iSelect == k)
            {
                SelectObject(hdc, lpBrushes[k + 7]);
            }
            else
            {
                SelectObject(hdc, lpBrushes[k]);
            }
            Rectangle
            (
                hdc,
                LEFT_COLUMN + i * COLUMN_INTERVAL - lpiPlateRadius[k],
                BASE_LINE - (j + 1) * PLATE_DISTANCE,
                LEFT_COLUMN + i * COLUMN_INTERVAL + lpiPlateRadius[k],
                BASE_LINE - (j + 1) * PLATE_DISTANCE + PLATE_THICKNESS
            );
        }
    }
}

void CCanvas::DrawRoundPlates(HDC hdc)
{
    int i, j, k;
    SelectObject(hdc, GetStockObject(BLACK_PEN));
    for (i = 0; i < 3; ++i)
    {
        for (j = 0; j < hanoi.m_lpiHeight[i]; ++j)
        {
            k = hanoi.m_lpiColumn[i * MAX_LAYERS + j];
            if (iSelect == k)
            {
                SelectObject(hdc, lpBrushes[k + 7]);
            }
            else
            {
                SelectObject(hdc, lpBrushes[k]);
            }
            RoundRect
            (
                hdc,
                LEFT_COLUMN + i * COLUMN_INTERVAL - lpiPlateRadius[k],
                BASE_LINE - (j + 1) * PLATE_DISTANCE,
                LEFT_COLUMN + i * COLUMN_INTERVAL + lpiPlateRadius[k],
                BASE_LINE - (j + 1) * PLATE_DISTANCE + PLATE_THICKNESS,
                PLATE_THICKNESS,
                PLATE_THICKNESS
            );
        }
    }
}

void CCanvas::DrawHand(HDC hdc)
{
    POINT lpVertices[7];
    lpVertices[0].x = LEFT_COLUMN + iHand * COLUMN_INTERVAL;
    lpVertices[0].y = BASE_LINE - hanoi.m_lpiHeight[iHand] * PLATE_DISTANCE;
    lpVertices[1].x = lpVertices[0].x + ARROW_RADIUS;
    lpVertices[1].y = lpVertices[0].y - ARROW_HEIGHT;
    lpVertices[2].x = lpVertices[0].x + HANDLE_RADIUS;
    lpVertices[2].y = lpVertices[0].y - ARROW_HEIGHT;
    lpVertices[3].x = lpVertices[0].x + HANDLE_RADIUS;
    lpVertices[3].y = lpVertices[0].y - HANDLE_LENGTH;
    lpVertices[4].x = lpVertices[0].x - HANDLE_RADIUS;
    lpVertices[4].y = lpVertices[0].y - HANDLE_LENGTH;
    lpVertices[5].x = lpVertices[0].x - HANDLE_RADIUS;
    lpVertices[5].y = lpVertices[0].y - ARROW_HEIGHT;
    lpVertices[6].x = lpVertices[0].x - ARROW_RADIUS;
    lpVertices[6].y = lpVertices[0].y - ARROW_HEIGHT;
    SelectObject(hdc, GetStockObject(BLACK_PEN));
    SelectObject(hdc, GetStockObject(WHITE_BRUSH));
    Polygon(hdc, lpVertices, 7);
}

void CCanvas::DrawMark(HDC hdc)
{
    RECT rect;
    int width, height;
    _TCHAR lpstrText[256];
    width = 240, height = 40;
    wsprintf(lpstrText, _T("A"));
    SelectObject(hdc, m_hFont);
    rect.left = LEFT_COLUMN - width / 2;
    rect.top = MARK_TOP;
    rect.bottom = MARK_TOP + height;
    rect.right = LEFT_COLUMN + width / 2;
    DrawText(hdc, lpstrText, 1, &rect, DT_TOP | DT_CENTER);
    wsprintf(lpstrText, _T("B"));
    rect.left = LEFT_COLUMN + COLUMN_INTERVAL - width / 2;
    rect.right = LEFT_COLUMN + COLUMN_INTERVAL + width / 2;
    DrawText(hdc,lpstrText, 1, &rect, DT_TOP | DT_CENTER);
    wsprintf(lpstrText, _T("C"));
    rect.left = LEFT_COLUMN + COLUMN_INTERVAL * 2 - width / 2;
    rect.right = LEFT_COLUMN + COLUMN_INTERVAL * 2 + width / 2;
    DrawText(hdc, lpstrText, 1, &rect, DT_TOP | DT_CENTER);
    wsprintf(lpstrText, _T("Steps: %d/%d"), hanoi.m_lpiSteps[0], hanoi.m_lpiSteps[1]);
    rect.left = STEP_MID - width / 2;
    rect.top = STEP_TOP;
    rect.right = STEP_MID + width / 2;
    rect.bottom = STEP_TOP + height;
    DrawText(hdc, lpstrText, strlen(lpstrText), &rect, DT_TOP | DT_CENTER);
}


void CCanvas::OnPaint(HWND hWnd, HDC hdc)
{
    RECT rect;
    GetClientRect(hWnd, &rect);
    SelectObject(hdc, GetStockObject(WHITE_BRUSH));
    SelectObject(hdc, GetStockObject(WHITE_PEN));
    Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
    DrawBases(hdc);
    switch (iPlateShape)
    {
    case ID_RECTANGLE:
        DrawRectangularPlates(hdc);
        break;
    case ID_ROUND:
        DrawRoundPlates(hdc);
        break;
    }
    DrawHand(hdc);
    DrawMark(hdc);
}

int CCanvas::SetLayers(int iLayers, HWND hWnd)
{
    int iResult, iChoice;
    if (hanoi.m_lpiSteps[1] > 0)
    {
        iChoice = MessageBox(hWnd, _T("The records will be discarded!\nWill you continue?"), _T("Warning"), MB_OKCANCEL);
        if (iChoice == 1)
        {
            hanoi.SetLayers(iLayers);
            iResult = iLayers;
            iHand = 0;
        }
    }
    else
    {
        hanoi.SetLayers(iLayers);
        iResult = iLayers;
        iHand = 0;
    }
    return iResult;
}

void CCanvas::Clear(HWND hWnd)
{
    int iChoice;
    if (hanoi.m_lpiSteps[1] > 0)
    {
        iChoice = MessageBox(hWnd, _T("The records will be discarded!\nWill you continue?"), _T("Warning"), MB_OKCANCEL);
        if (iChoice == 1)
        {
            iHand = 0;
            hanoi.Init();
        }
    }
    else
    {
        iHand = 0;
        hanoi.Init();
    }
}

void CCanvas::Solve(HWND hWnd)
{
    int iChoice;
    if (hanoi.m_lpiSteps[1] > 0)
    {
        iChoice = MessageBox(hWnd, _T("The records will be discarded!\nWill you continue?"), _T("Warning"), MB_OKCANCEL);
        if (iChoice == 1)
        {
            iHand = 0;
            hanoi.Init();
            hanoi.GenerateAuto();
        }
    }
    else
    {
        iHand = 0;
        hanoi.Init();
        hanoi.GenerateAuto();
    }
}

bool CCanvas::MoveLeft(HWND hWnd)
{
    int iDest, iChoice, iCase, iBackUp;
    bool bMove;
    iDest = (iHand + 2) % 3;
    if (iSelect != -1)
    {
        iCase = hanoi.TestMove(iHand, iDest);
        switch (iCase)
        {
        case 0:
            hanoi.Forward();
            iHand = iDest;
            break;
        case 1:
            if (hanoi.m_lpiSteps[0] < hanoi.m_lpiSteps[1])
            {
                iChoice = MessageBox(hWnd, _T("The previous records will be written!\nWill you continue?"), _T("Warning"), MB_OKCANCEL);
                if (iChoice == 1)
                {
                    hanoi.MoveWithRecord(iHand, iDest);
                    iHand = iDest;
                    bMove = true;
                }
            }
            else
            {
                hanoi.MoveWithRecord(iHand, iDest);
                iHand = iDest;
                bMove = true;
            }
            break;
        }
        iSelect = -1;
    }
    else
    {
        if (hanoi.m_lpiHeight[iDest] > 0)
        {
            iHand = iDest;
        }
        else
        {
            iBackUp = (iHand + 1) % 3;
            if (hanoi.m_lpiHeight[iBackUp] > 0)
            {
                iHand = iBackUp;
            }
        }
    }
    return bMove;
}

bool CCanvas::MoveRight(HWND hWnd)
{
    int iDest, iChoice, iCase, iBackUp;
    bool bMove;
    iDest = (iHand + 1) % 3;
    if (iSelect != -1)
    {
        iCase = hanoi.TestMove(iHand, iDest);
        switch (iCase)
        {
        case 0:
            hanoi.Forward();
            iHand = iDest;
            break;
        case 1:
            if (hanoi.m_lpiSteps[0] < hanoi.m_lpiSteps[1])
            {
                iChoice = MessageBox(hWnd, _T("The previous records will be written!\nWill you continue?"), _T("Warning"), MB_OKCANCEL);
                if (iChoice == 1)
                {
                    hanoi.MoveWithRecord(iHand, iDest);
                    iHand = iDest;
                    bMove = true;
                }
            }
            else
            {
                hanoi.MoveWithRecord(iHand, iDest);
                iHand = iDest;
                bMove = true;
            }
            break;
        }
        iSelect = -1;
    }
    else
    {
        if (hanoi.m_lpiHeight[iDest] > 0)
        {
            iHand = iDest;
        }
        else
        {
            iBackUp = (iHand + 2) % 3;
            if (hanoi.m_lpiHeight[iBackUp] > 0)
            {
                iHand = iBackUp;
            }
        }
    }
    return bMove;
}

int CCanvas::OnKeyUp(char cKeyCode, HWND hWnd)
{
    int iDest, iResult;
    bool bMove;
    bMove = false;
    iResult = -1;
    switch(cKeyCode)
    {
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
        iResult = SetLayers(cKeyCode - '0', hWnd);
        break;
    case VK_NUMPAD1:
    case VK_NUMPAD2:
    case VK_NUMPAD3:
    case VK_NUMPAD4:
    case VK_NUMPAD5:
    case VK_NUMPAD6:
    case VK_NUMPAD7:
        iResult = SetLayers(cKeyCode - '0', hWnd);
        break;
    case 'F':
        iDest = hanoi.Forward();
        if (iDest != -1)
        {
            iHand = iDest;
        }
        break;
    case 'B':
        iDest = hanoi.Backward();
        if (iDest != -1)
        {
            iHand = iDest;
        }
        break;
    case 'R':
    case 'r':
        Clear(hWnd);
        break;
    case 'A':
        Solve(hWnd);
        break;
    case VK_UP:
        iSelect = -1;
        break;
    case VK_DOWN:
        iSelect = hanoi.m_lpiColumn[iHand * MAX_LAYERS + hanoi.m_lpiHeight[iHand] - 1];
        break;
    case VK_LEFT:
        MoveLeft(hWnd);
        break;
    case VK_RIGHT:
        MoveRight(hWnd);
        break;
    }
    InvalidateRect(hWnd, NULL, true);
    if (bMove && hanoi.m_lpiHeight[2] == hanoi.m_nLayers)
    {
        MessageBox(hWnd, _T("Congratulations! You win!"), _T("Tip"), MB_OK);
        bMove = false;
    }
    return iResult;
}


